/**
 * @Title:ImproveB.java
 * @Description:TODO
 * @author shan
 * @date 2015-4-11 上午9:57:59
 */
package com.shan.ai.combo.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.shan.ai.combo.BaseCombo;
import com.shan.ai.combo.ICombo;
import com.shan.ai.combo.IComboConstant;
import com.shan.ai.combo.IComboConstant.BeadList;
import com.shan.ai.combo.IComboConstant.BeadType;
import com.shan.ai.combo.IComboConstant.Direct;
import com.shan.ai.combo.IComboConstant.Point;

/**
 * @author ShanSheng
 * 
 */
public class ImproveB extends BaseCombo implements ICombo, IComboConstant {

    private ThreadPoolExecutor executor = null;

    private Lock lock;

    private boolean bFinalStop;

    public long count;

    public double maxCost;

    private final int CORE_SIZE = 4;
    private final int MAX_CORE_SIZE = 4;
    private final long KEEP_ALIVE_TIME = 10;
    private final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
    private final BlockingQueue<Runnable> WORKING_QUEUE = new ArrayBlockingQueue<Runnable>(30);

    /**
     * @param args
     */
    public static void main(String[] args) {

         while(doWork()){
             try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         }
    }
    
    public static boolean doWork(){
        System.out.println("Start Work");
        ImproveB board = new ImproveB();

        long t1 = System.currentTimeMillis();

        // board.genRandomBoard();
        board.printBorad(board.initBeads);

        board.move(board.initBeads);

        // board.printList();

        // board.printBorad(board.maxBeads);
        // // board.computeCombo(board.maxBeads);
        //
        long t2 = System.currentTimeMillis();

        System.out.println("Time(s):" + (t2 - t1));
        // System.out.println("Combo:" + board.comboList.size());
        System.out.println("Move:" + board.count);
        System.out.println("Cost(s):" + board.maxCost);
        
        if(board.maxStepList == null || board.maxStepList.size()<3){
            return false;
        }

        for (int i = 0; i < board.maxStepList.size(); i++) {
            System.out.print(board.maxStepList.get(i) + "->");
        }

        System.out.println();

        board.printBorad(board.maxBeads);

        for (BeadList combo : board.maxcomboList) {
            System.out.print(String.format("%-8s", combo.type.toString()) + ":");
            for (int i = 0; i < combo.size(); i++) {
                System.out.print(combo.get(i) + "->");
            }

            System.out.println();
        }

        // ScreenCapUtils.drawLines(board.maxStepList, uuid + "_" +
        // String.valueOf(board.maxCost));
        ScreenParser.drawLinesOnDevice(board.maxStepList);
        
//        ScreenCapUtils.touchLines(board.maxStepList);
        
        System.out.println("End Work");
        return true;
    }

    public ImproveB() {
        initBeads = initBoard();
        startTime = System.currentTimeMillis();
        bFinalStop = false;
        maxCost = 1;
        count = 0;
        lock = new ReentrantLock();
    }

    @Override
    public List<Point> move(BeadType[][] initBeads) {
        startTime = System.currentTimeMillis();

        executor = new ThreadPoolExecutor(CORE_SIZE, MAX_CORE_SIZE, KEEP_ALIVE_TIME, TIME_UNIT, WORKING_QUEUE);

        for (int i = MAX_ROW - 1; i >= 0; i--) {
            for (int j = 0; j < MAX_COLUMN; j++) {

                runTimeBeads = copyBoard(initBeads);

                Point startPoint = new Point(i, j);
                executor.execute(new WorkThread(startPoint));

            }
        }

        executor.shutdown();

        try {
            boolean loop = true;
            do { // 等待所有任务完成
                loop = !executor.awaitTermination(30, TimeUnit.SECONDS); // 阻塞，直到线程池里所有任务结束

                if (bFinalStop) {
                    executor.shutdownNow();
                    System.out.println("ShutDown Now!");
                }
            } while (loop);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // printBorad(maxBeads);
        // WorkThread wt = new WorkThread(null);
        // wt.computeComboAdvance(maxBeads);

        return maxStepList;
    }

    class WorkThread implements Runnable {

        private Point startPoint;
        private BeadType[][] runTimeBeads;
        private List<Point> stepList;

        public WorkThread(Point startPoint) {
            this.startPoint = startPoint;
            stepList = new ArrayList<Point>();
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            Thread.currentThread().setName("[" + startPoint.x + "," + startPoint.y + "]");

            runTimeBeads = copyBoard(initBeads);
            stepList.add(startPoint);

            moveIn();

            System.out.println(Thread.currentThread().getName() + "=End at(s):" + (System.currentTimeMillis() - startTime));

        }

        public void moveIn() {

            if (bFinalStop) {
                return;
            }

            // 终止条件满足
            if (isEndCondition()) {
                bFinalStop = true;
                return;
            }

            if (stepList.size() >= MAX_STEP) {
                // bOnceStop = true;
                moveBack();

                return;
            }

            Point currPoint = stepList.get(stepList.size() - 1);

            for (Direct direct : Direct.values()) {

                double currCost = moveAndcost(currPoint, direct);
                // printList();
                // cost大于0代表移动有效。当移动两步时，如果还没有产生Combo，则退回。
                // 认定前两步移动能够产生Comobo才有可能是最优解。
                if ((currCost >= 0 && stepList.size() < 4) || (currCost > 0 && stepList.size() >= 4)) {
                    moveIn();
                    if (bFinalStop) {
                        return;
                    }
                } else if (currCost >= 0) {
                    moveBack();
                }

            }

            moveBack();

            return;
        }

        public void moveBack() {
            if (stepList.size() < 2) {
                return;
            }
            Point from = stepList.remove(stepList.size() - 1);
            Point to = stepList.get(stepList.size() - 1);

            BeadType temp = runTimeBeads[from.x][from.y];
            runTimeBeads[from.x][from.y] = runTimeBeads[to.x][to.y];
            runTimeBeads[to.x][to.y] = temp;
        }

        public boolean isEndCondition() {

            if (maxCost > MAX_COST) {
                System.out.println("局面值达到");
                return true;
            }

            if (System.currentTimeMillis() - startTime > OVER_TIME) {
                System.out.println("时间达到");
                return true;
            }

            return false;
        }

        public double moveAndcost(Point from, Direct direct) {

            Point to = null;
            switch (direct) {
            case up:
                to = new Point(from.x - 1, from.y);
                break;
            case down:
                to = new Point(from.x + 1, from.y);
                break;
            case left:
                to = new Point(from.x, from.y - 1);
                break;
            case right:
                to = new Point(from.x, from.y + 1);
                break;

            default:
                break;
            }

            if (to.x < 0 || to.x >= MAX_ROW || to.y < 0 || to.y >= MAX_COLUMN) {
                // cost = -1;
                return -1;
            }

            if (stepList.contains(to)) {
                return -1;
            }

            count++;

            stepList.add(to);

            BeadType temp = runTimeBeads[from.x][from.y];
            runTimeBeads[from.x][from.y] = runTimeBeads[to.x][to.y];
            runTimeBeads[to.x][to.y] = temp;

            List<BeadList> comboList = computeCombo(runTimeBeads);
            double cost = computeCost(comboList);

            if (cost >= maxCost) {
                lock.lock();

                try {

                    if ((cost > maxCost || (maxStepList != null && cost == maxCost && stepList.size() < maxStepList.size())) && filterLink(comboList)) {
                        maxCost = cost;
                        maxStepList = new ArrayList<IComboConstant.Point>(stepList);
                        maxcomboList = new ArrayList<IComboConstant.BeadList>(comboList);
                        System.out.println(Thread.currentThread().getName() + "=Update Max Cost:" + cost + " [" + (System.currentTimeMillis() - startTime)
                                + "] Combo:" + maxcomboList.size());

                        maxBeads = copyBoard(runTimeBeads);
                    }

                } finally {
                    lock.unlock();
                }
            }

            return cost;
        }

    }

    public boolean filterLink(List<BeadList> list) {

        boolean bFire = false;
        boolean bWater = false;
        boolean bWood = false;
        boolean bHeart = false;
        boolean bLight = false;
        boolean bDark = false;
        for (BeadList beadList : list) {
            if (beadList.type == BeadType.fire) {// && beadList.size()>=5
                bFire = true;
            }
            if (beadList.type == BeadType.water) {
                bWater = true;
            }
            if (beadList.type == BeadType.wood) {
                bWood = true;
            }
            if (beadList.type == BeadType.heart) {
                bHeart = true;
            }
            if (beadList.type == BeadType.light) {
                bLight = true;
            }
            if (beadList.type == BeadType.dark) {
                bDark = true;
            }
        }

        return true;
    }

}
